/*
 * Utilidad.java
 *
 * Created on 17 de mayo de 2008, 01:33 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package com.pragmaticminds.forrest.model;
import com.pragmaticminds.forrest.game.*;
import com.pragmaticminds.forrest.geometry.Circle;
import com.pragmaticminds.forrest.geometry.Function;
import com.pragmaticminds.forrest.geometry.Line;
import com.pragmaticminds.forrest.geometry.Position;
import com.pragmaticminds.forrest.utils.Types.*;
import com.pragmaticminds.forrest.model.World;

/**
 *
 * @author Pablo
 */
public class Utility {
    
    //Metodo getUtilidad
    private static double K1 = 55;
    private static double K2 = 35;
    private static double K3 = 10;

    //Metodo getFScore
    private static double angMinOptimal = 30;

    //Metodo getFOppCercano
    private static double distMaxOpp = 5;	
    private static double distMinOpp = 1.5;
    private static double distMinGoalkeeper = 2.2;

    //Metodo getFCantOpp
    private static double angCone = 30; //(es la mitad del cono, si el valor es 30 el cono es de 60)
    private static double distCone = 10;

    
    /** Creates a new instance of Utilidad */
    public Utility() {
    }
    
    //Este metodo devuelve el valor maximo de la funcion de utilidad
    public static double getMaxUtility(){
             return(100.0);
    }

    //Este metodo devuelve el valor minimo de la funcion de utilidad
    public static double getMinUtility(){
            return(0.0);
    }

    public static double getUtility(Position pos){
        return getUtility(pos, null);
    }
    
    public static double getUtility(Position pos, Double angPateo){
	double	utilidad, fDistGoal, fScore, fOppCercano, fCantOpp;
	
	fOppCercano = getFOppCercano(pos);
	fCantOpp = getFCantOpp(pos);
	
	if (angPateo != null){
		fScore = getFScore(pos, angPateo);
		utilidad = K1*fScore + K2*fOppCercano + K3*fCantOpp;
//		Log.log( UtiLog, "K1 * %9.5f + K2 * %9.5f + K3 * %9.5f - AngScore: %9.5f", fScore, fOppCercano, fCantOpp, *angPateo );
	}else{
		fDistGoal = getFDistGoal( pos );
		utilidad = K1*fDistGoal + K2*fOppCercano + K3*fCantOpp;		
//		Log.log( UtiLog, "K1 * %9.5f + K2 * %9.5f + K3 * %9.5f ", fDistGoal, fOppCercano, fCantOpp );		
	}
		
	return utilidad;
    }    
    
    private static double getFOppCercano(Position pos){
	//constantes ajustables: distMaxOpp, distMinOpp, distMinGolero
	int nroJugadorMasCercano = 0;
	double distanciaMasCercano = 0;
	
        Player op = World.getInstance().getOpponentNearness(pos);
        distanciaMasCercano = pos.getDistanceTo(op.getActualPosition());
        nroJugadorMasCercano = op.getNro();
        
	if ( distanciaMasCercano >= distMaxOpp ){
            //Si la distancia del oponente mas cercano es mayor o igual a 5 => devuelve 1
            return 1;	
	}else if ( distanciaMasCercano < distMinOpp || nroJugadorMasCercano == 1 && distanciaMasCercano < distMinGoalkeeper ){
            //Si la distancia del oponente mas cercano es menor a distMinOpp || 
            //el oponente mas cercano es el arquero y la distancia es menor a distMinGolero => se devuelve 0
            return 0;
	}else {
            //Se calcula el valor segun la formula distOponCerca / distMaxOpp;
            return distanciaMasCercano/distMaxOpp;		
	}		
    }

    private static double getFCantOpp(Position pos){
	//constantes ajustables: angCono, distCono

	Position posSol;
        Position posSol1 = null;
        Position posSol2 = null;
        
	Circle c = new Circle(pos,distCone);
	Line l = Line.makeLineFromTwoPoints(pos, World.getInstance().getCenterPosGoalOpponents());
	int cantOpp;
	
	//calculo el otro pto para definir el cono. Esta a "distCono" mts de pos y en direccion al arco contrario 
	l.getCircleIntersectionPoints(c, posSol1, posSol2);
	
	if ( posSol1.getPositionX() > posSol2.getPositionX() )
		posSol = posSol1;
	else
		posSol = posSol2;
		
	//calculo la cantidad de jugadores que hay en un cono con centro en pos, angulo "angCono" y dist "distCono" mts en direccion al arco contrario	
        cantOpp = World.getInstance().getCountOpponentInCone(Function.Deg2Rad(angCone), pos, posSol);	
	return (11 - cantOpp)/11;
    }

    private static double getFDistGoal(Position pos){
	//Se calcula la distancia al arco contrario. Este valor se encuentra entre 0 y 100 (mts)
	double 	distGoal = pos.getDistanceTo(World.getInstance().getCenterPosGoalOpponents());
	return 1/Math.exp(0.01*distGoal);	
	
	//Al multiplicar por 0.01 logramos que el valor se encuentre entre 0 y 1. 
	//Al aplicar la exponencial logramos que el valor se encuentre entre 1 y 2,71
	//Al dividir 1 por el valor anterior logramos tener un valor entre 0.36 y 1 (porcentaje)
	
	//Si dividimos la cancha cada 10 mts, tenemos que entre 100 y 50 mts de distancia al arco contrario el valor aumenta en promedio
	//5% cada 10 mts. Entre los 50 y 0 mts de distancia al arco contrario el valor aumenta en promedio 10% cada 10 mts.
    }    
    
    public static double getFScore(Position pos, double angPateo){
	//constantes ajustables: angMinOptimo	
	double mejorAngulo = Math.abs(getAngScore(pos, angPateo));
	//Log.log( UtiLog, "ANGULO PATEO: %f", mejorAngulo);	
	if ( mejorAngulo > angMinOptimal )	//Si el angulo es mayor a angMinOptimo => devolvemos el valor maximo
            return 1;
	else
            return ( mejorAngulo/180 );
    }
    
    private static double getAngScore(Position pos, double angPateo){     
	Position paloD = null, paloI = null;        
	double miAngulo, angGolero;
	// Para hacer el calculo de este angulo:
	// Basicamente tengo 2 formas de calcular este angulo:
	//	a) Usando el inverso del coseno del producto de los vectores normalizados
	//	b) Haciendo la proyeccion ortogonal de un vertice sobre el lado opusto para generar x e y y luego hallar arctg(y/x)
	// Para hacer el calculo del angulo necesito la posicion de los palos del arco relativos al objeto. Para esto tengo dos opciones
	//	1- sacar del modelo del mundo la posicion relativa de los dos palos ( objectT->getRelativePosition() ).Con esto corro el riesgo de que la posicion no este
	//		actualizada debido a que es informacion del WM.
	//	2- sacar la posicion obsoluta de los palos dado que son flags fijas y es informacion mas certera

	//	Elijo la opcion 2 por razones obvias
        
        World.getInstance().getPosGoalOpponents(paloI, paloD);        

	double angPaloI = paloI.subtract(pos).getDirection();
	double angPaloD = paloD.subtract(pos).getDirection();
	double distPaloI = paloI.subtract(pos).getMagnitude();
	double distPaloD = paloD.subtract(pos).getMagnitude();

	Position posGoalie = World.getInstance().getOpponent(1).getActualPosition();
	
	boolean conArquero=true;
	double bisector;
	double distArquero;
	
        angGolero = posGoalie.subtract(pos).getDirection();
        distArquero = posGoalie.subtract(pos).getMagnitude();

        if ((angGolero>angPaloD) || (angGolero<angPaloI))
                conArquero=false;

	double distCatOp;
	double tgArquero;
//            Log.log(2300, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//            Log.log(UtiLog, "Palo D X= %f Y= %f ", paloD.getX(), paloD.getY());//, paloIrelNor, paloDrelNor 
//            Log.log(UtiLog, "Palo I X= %f Y= %f ", paloI.getX(), paloI.getY());//, paloIrelNor, paloDrelNor 
//            Log.log(UtiLog, "PosObjeto X= %f Y= %f ", posObject.getX(), posObject.getY());//, paloIrelNor, paloDrelNor 
//            Log.log(UtiLog, "Angulo al Palo D: %f Angulo al Palo I: %f ", angPaloD, angPaloI );//, paloIrelNor, paloDrelNor 
//            if	(conArquero)	
//            {
//                    Log.log(UtiLog, "El arquero esta al medio  con Angulo: %f ", angGolero);//, paloIrelNor, paloDrelNor 
//            }
//            else
//                    Log.log(UtiLog, "El arquero NO esta al medio  con Angulo: %f ", angGolero);//, paloIrelNor, paloDrelNor

	bisector = Function.getBisectorTwoAngles( angPaloI, angPaloD );
   
	if (conArquero){
            //calculo la tangente
            tgArquero = Math.abs(Function.Rad2Deg(Math.atan(2.2/distArquero)));
//          Log.log(UtiLog, "Distancia al Arquero: %f Tangente: %f ", distArquero, tgArquero);//, paloIrelNor, paloDrelNor 

            if (bisector>angGolero){
                //pateo al palo derecho
//                    Log.log(UtiLog, "pateo al palo derecho");
                double angMin=angGolero+tgArquero;
                miAngulo = angPaloD - angMin;
                bisector = Function.getBisectorTwoAngles( angMin, angPaloD );
                if (miAngulo>=30)
                        bisector = Function.getBisectorTwoAngles( bisector ,angPaloD);
            }
            else{
                //pateo al palo izquierdo
//              Log.log(UtiLog, "pateo al palo izquierdo");
//              Log.log(UtiLog, "pateo al palo derecho");
                double angMax=angGolero-tgArquero;
                miAngulo = angMax - angPaloI;
                bisector = Function.getBisectorTwoAngles( angPaloI ,angMax);                    
                if (miAngulo>=30)
                        bisector = Function.getBisectorTwoAngles( angPaloI ,bisector);		
            }
	}
	else{
            if (distPaloI>distPaloD){   // la hipotenusa es la màs larga!!!		
                Line lPaloMayor = Line.makeLineFromTwoPoints(pos, paloI);
                distCatOp = lPaloMayor.getDistanceWithPoint(paloD);
                miAngulo = Math.abs(Function.Rad2Deg(Math.asin(distCatOp/distPaloD)));
                if (miAngulo>=30)
                    if (posGoalie.getPositionY()>pos.getPositionY())
                            bisector = Function.getBisectorTwoAngles( bisector, angPaloD);	
                    else	
                            bisector = Function.getBisectorTwoAngles( angPaloI, bisector);						
            }
            else{
                Line lPaloMayor  = Line.makeLineFromTwoPoints(pos, paloD);
                distCatOp = lPaloMayor.getDistanceWithPoint(paloI);
                miAngulo = Math.abs(Function.Rad2Deg(Math.asin(distCatOp/distPaloI)));
                if (miAngulo>=30)
                    if (posGoalie.getPositionY()>pos.getPositionY())
                        bisector = Function.getBisectorTwoAngles( bisector, angPaloD);	
                    else	
                        bisector = Function.getBisectorTwoAngles( angPaloI, bisector);									
            }
//          Log.log(UtiLog, "Dist Palo I %f Dist Palo D %f ", distPaloI, distPaloD);//, paloIrelNor, paloDrelNor 
//          Log.log(UtiLog, "Dist Cateto Opuesto %f ", distCatOp);//, paloIrelNor, paloDrelNor 
//          Log.log(UtiLog, "Angulo de Score miAngulo %f ", miAngulo);//, paloIrelNor, paloDrelNor 
	}						
		 	
		
				
		
//		tapaGolero = isAngInInterval( angGolero, angPaloI, angPaloD );
//		if 
	
		
//	// Calculo el socreAngle (queda en grados)
//	scoreAngle = Rad2Deg(1/cos(paloDrelNor.getX()*paloIrelNor.getX() + 
//					paloDrelNor.getY()*paloIrelNor.getY() ) );
		

//	Log.log(UtiLog, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

//	Log.log(UtiLog, "Angulo Factible: %f ", miAngulo);//, paloIrelNor, paloDrelNor 
//	Log.log(UtiLog, "Bisector: %f ", bisector);//, paloIrelNor, paloDrelNor 

//	Log.log(UtiLog, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

	angPateo=bisector;
	return miAngulo;        
    }

}
